LANDING EDITOR COMPREHENSIVE PLAN Copy
Documentation for Plans
Landing Editor Comprehensive Implementation Plan
๐ Plan Overview
Goal: Fix and enhance the landing-editor component with Flash-persistence, localStorage-based user identification, AI assistance, and skip logic.
Alignment:
- Follow gograb-space patterns (auth storage, Flash-persistence keys, event system)
- Integrate gograb-ai capabilities (AI assistance for new users)
- Match gograb-space architecture (security, multi-tenant preparation)
๐ฏ Phase 1: Analysis & Architecture (CURRENT)
1.1 Architecture Alignment Analysis
gograb-space Patterns Identified:
- โ
AuthServiceuses localStorage for JWT/user storage (gograb_userkey) - โ NGXS for global state + Signals for component state
- โ
Event-driven architecture via
EventBusService - โ
Multi-tenant context via
ContextService(tenant-aware) - โ Security layer with guards, interceptors, directives
- โ Mock-API integration for development
- โ Structured module system with clear dependencies
gograb-ai Capabilities Available:
- โ Multi-provider AI (OpenAI, Anthropic, Google AI)
- โ
Self-contained chat components (
<lib-ai-chat chatId="...">) - โ Event-driven communication with @Output() events
- โ AI image generation (DALL-E, Claude Vision)
- โ Intelligent storage integration (IndexedDB per chatId)
- โ Real-time analytics and cost tracking
- โ Universal widget generator service
- โ Prompt management system
editor Monorepo Current State:
- โ
Storage-system with Flash-persistence (
autoFlashState) - โ Mock-API library ready
- โ gograb-ai fully integrated
- โ Landing component using manual localStorage
- โ No user identification system
- โ No AI assistance on landing page
1.2 Key Architectural Decisions
| Decision | Rationale | Implementation |
|---|---|---|
| User ID Storage | localStorage (like gograb-space JWT) | Synchronous access, auth migration ready |
| State Management | Flash-persistence (autoFlashState) | Automatic persistence, signal-based |
| AI Integration | gograb-ai self-contained | Enhanced onboarding, smart suggestions |
| Event Communication | EventBus pattern | Loosely coupled, scalable |
| Testing Strategy | Vitest (not build) | Faster feedback, serve-based analysis |
๐ง Phase 2: User Identification System
2.1 Create UserIdManager Service
Location: libs/storage-system/src/core/user-context/user-id-manager.ts
Requirements:
- Generate unique user ID on first app load
- Store in localStorage key:
gg_editor_user_id - Format:
user-{timestamp}-{randomId} - Provide signal-based access:
currentUserId(): Signal<string> - Prepare for Firebase Auth / JWT migration
- Match gograb-space pattern (localStorage for identity)
Implementation:
import { Injectable, signal, computed } from '@angular/core';
export interface UserIdentity {
userId: string;
createdAt: string;
type: 'simulated' | 'firebase' | 'jwt';
}
@Injectable({ providedIn: 'root' })
export class UserIdManager {
private readonly USER_KEY = 'gg_editor_user_id';
private readonly _identity = signal<UserIdentity | null>(null);
readonly currentUserId = computed(() => this._identity()?.userId ?? '');
readonly userIdentity = computed(() => this._identity());
constructor() {
this.initializeUser();
}
private initializeUser(): void {
const stored = localStorage.getItem(this.USER_KEY);
if (stored) {
try {
this._identity.set(JSON.parse(stored));
return;
} catch {
// Invalid stored data, create new
}
}
// Create new simulated user
const identity: UserIdentity = {
userId: `user-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
createdAt: new Date().toISOString(),
type: 'simulated'
};
localStorage.setItem(this.USER_KEY, JSON.stringify(identity));
this._identity.set(identity);
}
migrateToFirebase(firebaseUid: string): void {
// Future: Migration helper
const identity: UserIdentity = {
userId: firebaseUid,
createdAt: new Date().toISOString(),
type: 'firebase'
};
localStorage.setItem(this.USER_KEY, JSON.stringify(identity));
this._identity.set(identity);
}
}
Export: Update libs/storage-system/src/core/user-context/index.ts
2.2 Update provideGgStorage (Optional User Scoping)
Location: libs/storage-system/src/angular/providers/provide-gg-storage.ts
Add Configuration:
export interface GgStorageConfig {
namespace?: string;
cache?: CacheConfig;
strategy?: StorageStrategy;
userScoped?: boolean; // NEW: Enable user-scoped keys
}
// In provider function:
if (config.userScoped) {
const userIdManager = inject(UserIdManager);
// Intercept keys and prefix with user/{userId}/
// Example: flash:preferences โ flash:user/user-123/preferences
}
๐จ Phase 3: Flash-Persistence Integration
3.1 Convert Landing Component
Location: libs/editor-web-lib/src/lib/components/landing-editor/landing-editor.component.ts
Current Issues:
- Manual localStorage calls scattered
- No signal reactivity
- Tight coupling with storage implementation
New Implementation:
import { Component, signal, computed, inject, OnInit, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { autoFlashState } from '@storage-system';
import { ProjectDetectionService } from '../../services/project-detection.service';
interface LandingPreferences {
showLandingByDefault: boolean;
rememberLastProject: boolean;
visits: number;
lastVisit: string;
lastProjectId?: string;
}
interface RecentProject {
id: string;
name: string;
description: string;
lastModified: Date;
thumbnail?: string;
}
@Component({
selector: 'editor-landing',
standalone: true,
// ... imports
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './landing-editor.component.html',
styleUrls: ['./landing-editor.component.scss']
})
export class LandingComponent implements OnInit, OnDestroy {
private router = inject(Router);
private projectDetection = inject(ProjectDetectionService);
// ๐ฅ Flash-persistence for preferences
private landingPrefs = autoFlashState<LandingPreferences>(
'editor-preferences/landing',
{
showLandingByDefault: true,
rememberLastProject: false,
visits: 0,
lastVisit: new Date().toISOString()
}
);
// ๐ฅ Flash-persistence for recent projects
private recentProjectsState = autoFlashState<RecentProject[]>(
'editor-preferences/recent-projects',
[]
);
// Computed signals for UI
showLandingByDefault = computed(() => this.landingPrefs().showLandingByDefault);
rememberLastProject = computed(() => this.landingPrefs().rememberLastProject);
recentProjects = computed(() => this.recentProjectsState());
async ngOnInit() {
// Track visit
this.landingPrefs.update(prefs => ({
...prefs,
visits: prefs.visits + 1,
lastVisit: new Date().toISOString()
}));
// Check auto-skip
await this.checkShouldSkip();
// Load recent projects from storage
this.loadRecentProjects();
// Initialize animations
this.initializeParticleSystem();
this.initializeNeuralNetwork();
}
private async checkShouldSkip() {
const prefs = this.landingPrefs();
const hasProjects = await this.projectDetection.hasExistingProjects();
if (!prefs.showLandingByDefault && hasProjects) {
this.startEditing();
}
}
toggleLandingPreference() {
this.landingPrefs.update(prefs => ({
...prefs,
showLandingByDefault: !prefs.showLandingByDefault
}));
}
toggleRememberProject() {
this.landingPrefs.update(prefs => ({
...prefs,
rememberLastProject: !prefs.rememberLastProject
}));
}
onSkipLanding() {
this.landingPrefs.update(prefs => ({
...prefs,
showLandingByDefault: false
}));
this.startEditing();
}
// ... rest of component
}
Key Structure:
flash:editor-preferences/landing -> LandingPreferences
flash:editor-preferences/recent-projects -> RecentProject[]
flash:editor-web/pages -> Page data (for detection)
3.2 Verify Component Dependencies
ExecutionLogsComponent: โ Uses HttpClient only - no changes needed LiveActivityComponent: โ Uses UnifiedEventsService only - no changes needed
๐ Phase 4: Project Detection Service
4.1 Create ProjectDetectionService
Location: libs/editor-web-lib/src/lib/services/project-detection.service.ts
import { Injectable, inject } from '@angular/core';
import { GgStorage } from '@storage-system';
@Injectable({ providedIn: 'root' })
export class ProjectDetectionService {
private storage = inject(GgStorage);
async hasExistingProjects(): Promise<boolean> {
try {
// Check multiple possible keys
const keys = [
'flash:editor-web/pages',
'flash:editor-preferences/recent-projects'
];
for (const key of keys) {
const value = await this.storage.get(key);
if (Array.isArray(value) && value.length > 0) {
return true;
}
if (value && typeof value === 'object') {
const entries = Object.keys(value);
if (entries.length > 0) {
return true;
}
}
}
return false;
} catch (error) {
console.warn('ProjectDetection: Error checking projects', error);
return false;
}
}
async getLastProject(): Promise<string | null> {
try {
const prefs = await this.storage.get('flash:editor-preferences/landing');
return prefs?.lastProjectId || null;
} catch {
return null;
}
}
async getProjectCount(): Promise<number> {
try {
const value = await this.storage.get('flash:editor-web/pages');
if (Array.isArray(value)) {
return value.length;
}
if (value && typeof value === 'object') {
return Object.keys(value).length;
}
return 0;
} catch {
return 0;
}
}
}
Export: libs/editor-web-lib/src/lib/services/index.ts
๐ก๏ธ Phase 5: Landing Guard with Skip Logic
5.1 Create Landing Guard
Location: libs/editor-web-lib/src/lib/guards/landing.guard.ts
import { inject } from '@angular/core';
import { Router, CanActivateFn } from '@angular/router';
import { ProjectDetectionService } from '../services/project-detection.service';
import { GgStorage } from '@storage-system';
export const landingGuard: CanActivateFn = async () => {
const router = inject(Router);
const projectDetection = inject(ProjectDetectionService);
const storage = inject(GgStorage);
try {
// Load preferences
const prefs = await storage.get('flash:editor-preferences/landing');
// Check for existing projects
const hasProjects = await projectDetection.hasExistingProjects();
// Skip logic
if (prefs && !prefs.showLandingByDefault && hasProjects) {
router.navigate(['/editor/workspace']);
return false;
}
if (prefs && prefs.rememberLastProject && hasProjects) {
const lastProject = await projectDetection.getLastProject();
if (lastProject) {
router.navigate(['/editor/workspace'], {
queryParams: { pageId: lastProject }
});
return false;
}
}
// Show landing
return true;
} catch (error) {
console.warn('LandingGuard: Error in skip logic', error);
return true; // Show landing on error
}
};
Export: libs/editor-web-lib/src/lib/guards/index.ts
๐ค Phase 6: AI Integration (NEW)
6.1 Add AI Assistant to Landing
Purpose: Help new users get started with AI suggestions
Implementation in Landing Component:
// Add to LandingComponent
import { AiChatComponent } from '@gograb-ai';
// In component class
showAiHelper = signal(false);
aiChatId = 'landing-assistant';
// In template
@if (showAiHelper()) {
<div class="ai-helper-overlay">
<lib-ai-chat
[chatId]="aiChatId"
(configurationChanged)="onAiConfigChanged($event)"
(conversationStarted)="onConversationStarted()"
/>
</div>
}
// Button to toggle
<button
class="ai-help-button"
(click)="showAiHelper.set(!showAiHelper())">
<i class="fas fa-robot"></i>
AI Help
</button>
AI Helper Features:
- โ Quick start tutorials
- โ Personalized project suggestions
- โ Answer common questions
- โ Generate project templates with AI
6.2 AI-Powered Quick Actions
Implementation:
quickActions = computed<QuickAction[]>(() => [
{
id: 'new-widget',
title: 'Create Widget',
description: 'Start with a blank canvas and build your widget from scratch',
icon: '๐จ',
action: () => this.createNewWidget(),
color: 'primary'
},
{
id: 'ai-generate',
title: 'AI Generate',
description: 'Describe what you want and let AI create it for you',
icon: '๐ค',
action: () => this.openAiGenerator(),
color: 'secondary'
},
// ... more actions
]);
private async openAiGenerator() {
this.showAiHelper.set(true);
// Pre-fill with generation prompt
}
๐ Phase 7: Update Routes
7.1 Update Editor Routes
Location: libs/editor-web-lib/src/lib/editor.routes.ts
import { Routes } from '@angular/router';
import { landingGuard } from './guards/landing.guard';
export const EditorRoutes: Routes = [
{
path: '',
redirectTo: 'landing',
pathMatch: 'full'
},
{
path: 'landing',
loadComponent: () =>
import('./components/landing-editor/landing-editor.component')
.then(m => m.LandingComponent),
canActivate: [landingGuard],
title: 'Welcome to Editor'
},
{
path: 'workspace',
loadComponent: () =>
import('./components/editor-workspace/editor-workspace.component')
.then(m => m.EditorWorkspaceComponent),
title: 'Editor Workspace'
},
{
path: 'execution-logs',
loadComponent: () =>
import('./components/execution-logs/execution-logs.component')
.then(m => m.ExecutionLogsComponent),
title: 'Execution Logs'
},
{
path: 'widgets',
loadComponent: () =>
import('./components/widgets/widgets-panel.component')
.then(m => m.WidgetsPanelComponent),
title: 'Widgets'
}
];
๐งช Phase 8: Unit Testing
8.1 UserIdManager Tests
Location: libs/storage-system/src/core/user-context/user-id-manager.spec.ts
import { TestBed } from '@angular/core/testing';
import { UserIdManager } from './user-id-manager';
describe('UserIdManager', () => {
let service: UserIdManager;
beforeEach(() => {
localStorage.clear();
TestBed.configureTestingModule({});
service = TestBed.inject(UserIdManager);
});
it('should generate user ID on first load', () => {
expect(service.currentUserId()).toMatch(/^user-\d+-[a-z0-9]+$/);
});
it('should persist user ID across instances', () => {
const userId1 = service.currentUserId();
const service2 = TestBed.inject(UserIdManager);
expect(service2.currentUserId()).toBe(userId1);
});
it('should have signal reactivity', () => {
const userId = service.currentUserId();
expect(userId).toBeTruthy();
});
it('should handle Firebase migration', () => {
service.migrateToFirebase('firebase-uid-123');
expect(service.currentUserId()).toBe('firebase-uid-123');
expect(service.userIdentity()?.type).toBe('firebase');
});
});
8.2 ProjectDetectionService Tests
Location: libs/editor-web-lib/src/lib/services/project-detection.service.spec.ts
import { TestBed } from '@angular/core/testing';
import { ProjectDetectionService } from './project-detection.service';
import { GgStorage } from '@storage-system';
describe('ProjectDetectionService', () => {
let service: ProjectDetectionService;
let mockStorage: jasmine.SpyObj<GgStorage>;
beforeEach(() => {
mockStorage = jasmine.createSpyObj('GgStorage', ['get']);
TestBed.configureTestingModule({
providers: [
{ provide: GgStorage, useValue: mockStorage }
]
});
service = TestBed.inject(ProjectDetectionService);
});
it('should detect no projects when storage is empty', async () => {
mockStorage.get.and.returnValue(Promise.resolve([]));
const result = await service.hasExistingProjects();
expect(result).toBe(false);
});
it('should detect projects when array has items', async () => {
mockStorage.get.and.returnValue(Promise.resolve([{ id: '1' }]));
const result = await service.hasExistingProjects();
expect(result).toBe(true);
});
it('should detect projects when object has keys', async () => {
mockStorage.get.and.returnValue(Promise.resolve({ page1: {} }));
const result = await service.hasExistingProjects();
expect(result).toBe(true);
});
it('should handle storage errors gracefully', async () => {
mockStorage.get.and.returnValue(Promise.reject('Error'));
const result = await service.hasExistingProjects();
expect(result).toBe(false);
});
it('should get project count', async () => {
mockStorage.get.and.returnValue(Promise.resolve([1, 2, 3]));
const count = await service.getProjectCount();
expect(count).toBe(3);
});
});
8.3 Landing Guard Tests
Location: libs/editor-web-lib/src/lib/guards/landing.guard.spec.ts
import { TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { landingGuard } from './landing.guard';
import { ProjectDetectionService } from '../services/project-detection.service';
import { GgStorage } from '@storage-system';
describe('landingGuard', () => {
let mockRouter: jasmine.SpyObj<Router>;
let mockProjectDetection: jasmine.SpyObj<ProjectDetectionService>;
let mockStorage: jasmine.SpyObj<GgStorage>;
beforeEach(() => {
mockRouter = jasmine.createSpyObj('Router', ['navigate']);
mockProjectDetection = jasmine.createSpyObj('ProjectDetectionService', [
'hasExistingProjects',
'getLastProject'
]);
mockStorage = jasmine.createSpyObj('GgStorage', ['get']);
TestBed.configureTestingModule({
providers: [
{ provide: Router, useValue: mockRouter },
{ provide: ProjectDetectionService, useValue: mockProjectDetection },
{ provide: GgStorage, useValue: mockStorage }
]
});
});
it('should show landing for first-time users', async () => {
mockStorage.get.and.returnValue(Promise.resolve(null));
mockProjectDetection.hasExistingProjects.and.returnValue(Promise.resolve(false));
const result = await TestBed.runInInjectionContext(() => landingGuard());
expect(result).toBe(true);
});
it('should skip landing when disabled and has projects', async () => {
mockStorage.get.and.returnValue(Promise.resolve({
showLandingByDefault: false
}));
mockProjectDetection.hasExistingProjects.and.returnValue(Promise.resolve(true));
const result = await TestBed.runInInjectionContext(() => landingGuard());
expect(result).toBe(false);
expect(mockRouter.navigate).toHaveBeenCalledWith(['/editor/workspace']);
});
it('should navigate to last project when remember is enabled', async () => {
mockStorage.get.and.returnValue(Promise.resolve({
rememberLastProject: true
}));
mockProjectDetection.hasExistingProjects.and.returnValue(Promise.resolve(true));
mockProjectDetection.getLastProject.and.returnValue(Promise.resolve('page-123'));
const result = await TestBed.runInInjectionContext(() => landingGuard());
expect(result).toBe(false);
expect(mockRouter.navigate).toHaveBeenCalledWith(
['/editor/workspace'],
{ queryParams: { pageId: 'page-123' } }
);
});
});
8.4 Landing Component Integration Tests
Location: libs/editor-web-lib/src/lib/components/landing-editor/landing-editor.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LandingComponent } from './landing-editor.component';
import { ProjectDetectionService } from '../../services/project-detection.service';
import { Router } from '@angular/router';
describe('LandingComponent', () => {
let component: LandingComponent;
let fixture: ComponentFixture<LandingComponent>;
let mockProjectDetection: jasmine.SpyObj<ProjectDetectionService>;
let mockRouter: jasmine.SpyObj<Router>;
beforeEach(async () => {
mockProjectDetection = jasmine.createSpyObj('ProjectDetectionService', [
'hasExistingProjects'
]);
mockRouter = jasmine.createSpyObj('Router', ['navigate']);
await TestBed.configureTestingModule({
imports: [LandingComponent],
providers: [
{ provide: ProjectDetectionService, useValue: mockProjectDetection },
{ provide: Router, useValue: mockRouter }
]
}).compileComponents();
fixture = TestBed.createComponent(LandingComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should toggle landing preference', () => {
const initial = component.showLandingByDefault();
component.toggleLandingPreference();
expect(component.showLandingByDefault()).toBe(!initial);
});
it('should toggle remember project preference', () => {
const initial = component.rememberLastProject();
component.toggleRememberProject();
expect(component.rememberLastProject()).toBe(!initial);
});
it('should skip landing on button click', () => {
component.onSkipLanding();
expect(component.showLandingByDefault()).toBe(false);
expect(mockRouter.navigate).toHaveBeenCalled();
});
});
๐ Phase 9: Linter Analysis (Using Serve)
9.1 Start Development Server
# Start editor-web serve (port 5200)
nx serve editor-web
# OR start specific app if testing there
nx serve ux-test
9.2 Check Console for Errors
Monitor browser console for:
- โ No Flash-persistence errors
- โ No route resolution errors
- โ No component import errors
- โ Proper signal reactivity
- โ Storage operations working
9.3 Manual Testing Checklist
- Landing page loads without errors
- AI helper button appears and works
- Skip button sets preference correctly
- Toggle preferences persist across refreshes
- Guard redirects work correctly
- Recent projects display properly
- Particle system animates smoothly
- All quick actions work
๐ Phase 10: Documentation
10.1 Create Documentation Structure
Following @add-documentation.md guidelines:
Structure:
docs/english/GoGrab-Space/editor-web/
โโโ index.md (landing page overview)
โโโ landing-page.md (this implementation)
โโโ flash-persistence-integration.md
โโโ ai-integration.md
docs/spanish/GoGrab-Space/editor-web/
โโโ index.es.md
โโโ landing-page.es.md (placeholder)
10.2 Landing Page Documentation
Location: docs/english/GoGrab-Space/editor-web/landing-page.md
---
title: Landing Page Implementation
description: Complete guide to the editor landing page with Flash-persistence and AI integration
icon: ๐
order: 1
crossLanguageId: editor-landing-page
languageVersions:
en: GoGrab-Space/editor-web/landing-page
es: GoGrab-Space/editor-web/landing-page
---
# Landing Page Implementation
## Overview
The editor landing page provides a welcoming introduction to new users with automatic skip logic for returning users.
## Features
### Flash-Persistence Integration
The landing page uses `autoFlashState` for automatic state persistence:
\```typescript
private landingPrefs = autoFlashState<LandingPreferences>(
'editor-preferences/landing',
{
showLandingByDefault: true,
rememberLastProject: false,
visits: 0,
lastVisit: new Date().toISOString()
}
);
\```
### AI Assistant
New users can get help from an AI assistant:
\```typescript
<lib-ai-chat
[chatId]="'landing-assistant'"
(conversationStarted)="onConversationStarted()"
/>
\```
### Skip Logic
The landing guard automatically skips the landing page for returning users:
- Users with `showLandingByDefault: false` skip to workspace
- Users with `rememberLastProject: true` go to last project
- New users always see the landing page
## Configuration
### User Preferences
\```typescript
interface LandingPreferences {
showLandingByDefault: boolean;
rememberLastProject: boolean;
visits: number;
lastVisit: string;
lastProjectId?: string;
}
\```
## Usage
### Accessing the Landing Page
Navigate to `/editor/landing` to see the landing page.
### Skipping the Landing
Click the skip button or disable in preferences:
\```typescript
toggleLandingPreference() {
this.landingPrefs.update(prefs => ({
...prefs,
showLandingByDefault: !prefs.showLandingByDefault
}));
}
\```
## Architecture
### Storage Keys
- `flash:editor-preferences/landing` - User preferences
- `flash:editor-preferences/recent-projects` - Recent projects list
- `gg_editor_user_id` - User identification (localStorage)
### Services
- `ProjectDetectionService` - Detects existing projects
- `UserIdManager` - Manages user identification
- `LandingGuard` - Route guard with skip logic
## Best Practices
1. Always use Flash-persistence for user preferences
2. Test skip logic with multiple scenarios
3. Ensure AI assistant is non-intrusive
4. Monitor performance with particle animations
## See Also
- [Flash-Persistence Integration](/GoGrab-Space/editor-web/flash-persistence-integration)
- [AI Integration](/GoGrab-Space/editor-web/ai-integration)
- [Storage System](/GoGrab-Space/storage-system/overview)
10.3 Flash-Persistence Integration Docs
Location: docs/english/GoGrab-Space/editor-web/flash-persistence-integration.md
---
title: Flash-Persistence Integration
description: How the editor uses Flash-persistence for automatic state management
icon: โก
order: 2
---
# Flash-Persistence Integration
## Overview
The editor integrates Flash-persistence for automatic state persistence without manual localStorage management.
## Key Concepts
### autoFlashState
Replace manual localStorage with automatic persistence:
\```typescript
// โ Manual localStorage (old way)
localStorage.setItem('prefs', JSON.stringify(prefs));
// โ
Flash-persistence (new way)
const prefs = autoFlashState('editor-preferences/landing', defaultPrefs);
prefs.update(p => ({ ...p, visits: p.visits + 1 }));
\```
### Key Structure
All editor keys follow a consistent pattern:
- `flash:editor-preferences/*` - User preferences
- `flash:editor-web/pages/*` - Page data
- `flash:editor-ui/*` - UI state
## Examples
### User Preferences
\```typescript
const landingPrefs = autoFlashState('editor-preferences/landing', {
showLandingByDefault: true,
rememberLastProject: false
});
// Reactive UI
showLandingByDefault = computed(() => landingPrefs().showLandingByDefault);
\```
### Recent Projects
\```typescript
const recentProjects = autoFlashState<RecentProject[]>(
'editor-preferences/recent-projects',
[]
);
// Add project
recentProjects.update(projects => [...projects, newProject]);
\```
## Migration from localStorage
### Before
\```typescript
class PreferencesService {
private preferences = signal({ theme: 'dark' });
async loadPreferences() {
const stored = localStorage.getItem('preferences');
if (stored) this.preferences.set(JSON.parse(stored));
}
async savePreferences(prefs: Preferences) {
this.preferences.set(prefs);
localStorage.setItem('preferences', JSON.stringify(prefs));
}
}
\```
### After
\```typescript
class PreferencesService {
preferences = autoFlashState('preferences', { theme: 'dark' });
// That's it! No load/save needed
}
\```
## Performance
Flash-persistence provides:
- โก 0.3ms RAM cache reads
- ๐พ Automatic persistence (debounced)
- ๐ง Intelligent storage adapter selection
- ๐ Optional MongoDB sync
## See Also
- [Storage System Overview](/GoGrab-Space/storage-system/overview)
- [Flash-Persistence API](/GoGrab-Space/storage-system/flash-persistence/api-reference)
10.4 Create Index Files
Location: docs/english/GoGrab-Space/editor-web/index.md
---
title: Editor Web Library
description: Documentation for the editor-web-lib Angular library
icon: โ๏ธ
order: 1
---
# Editor Web Library
The `editor-web-lib` library provides the core editor functionality for the GoGrab Space platform.
## Features
- ๐ **Landing Page** - Welcoming introduction with AI assistance
- โ๏ธ **Editor Workspace** - Visual page editor
- ๐จ **Widget System** - Drag-and-drop widget builder
- โก **Flash-Persistence** - Automatic state management
- ๐ค **AI Integration** - Smart suggestions and generation
## Quick Start
\```typescript
// Import editor routes
import { EditorRoutes } from '@editor-web-lib';
// Add to app routes
export const routes: Routes = [
{
path: 'editor',
children: EditorRoutes
}
];
\```
## Documentation
- [Landing Page](/GoGrab-Space/editor-web/landing-page)
- [Flash-Persistence Integration](/GoGrab-Space/editor-web/flash-persistence-integration)
- [AI Integration](/GoGrab-Space/editor-web/ai-integration)
## Architecture
The editor follows a modular architecture with clear separation:
- **Components** - UI components with OnPush strategy
- **Services** - Business logic and state management
- **Guards** - Route protection and skip logic
- **State** - Flash-persistence for automatic persistence
โ Success Criteria
Functionality
- Landing component uses Flash-persistence (no manual localStorage)
- User identification system works with localStorage
- Skip logic correctly routes returning users
- Landing route is re-enabled with guard
- AI helper provides useful assistance
- All components load without errors
Testing
- All unit tests pass (>80% coverage)
- Integration tests verify end-to-end flows
- Manual testing checklist completed
- No errors in serve console
Documentation
- Landing page guide created
- Flash-persistence integration documented
- AI integration examples provided
- Migration guide from localStorage
- Spanish placeholders created
Performance
- Landing page loads < 1s
- Particle animations run smoothly
- Flash-persistence operations < 5ms
- No memory leaks detected
๐ Execution Order
- โ Analysis Complete (Phase 1)
- โณ Create UserIdManager (Phase 2.1) - NEXT
- โณ Update provideGgStorage (Phase 2.2)
- โณ Convert Landing Component (Phase 3)
- โณ Create ProjectDetectionService (Phase 4)
- โณ Create Landing Guard (Phase 5)
- โณ Add AI Integration (Phase 6)
- โณ Update Routes (Phase 7)
- โณ Create Unit Tests (Phase 8)
- โณ Linter Analysis (Phase 9)
- โณ Create Documentation (Phase 10)
๐ Implementation Tracking
Track progress with todo items linked to this plan.
Start Date: Today Target Completion: Based on execution pace Approach: Iterative with continuous testing via serve